#pragma once

#include "object.h"

SYS_BEGIN()

class ObjectPtr;

template<class IF_Type>
class InterfacePtr
{
	typedef InterfacePtr<IF_Type> thisClass;
public:
	enum{IID_IF=IF_Type::IID_IF};

public:
	InterfacePtr(IF_Type* pInterface=NULL):
			m_pInterface(pInterface)
		{
			if(m_pInterface)
				m_pInterface->IncInterfaceRef();
		}
	template<typename TOtherInterface>
	explicit InterfacePtr(const InterfacePtr<TOtherInterface>& pInterface)
		:m_pInterface(NULL)
		{
			if (pInterface){
				IObject* pObject = NULL;
				pInterface->QueryObject(&pObject);
				QueryVInterface(pObject);
			}
		}
	explicit InterfacePtr(const thisClass& o) :
				m_pInterface(o.m_pInterface)
			{
				if(m_pInterface)
					m_pInterface->IncInterfaceRef();
			}
	explicit InterfacePtr(const ObjectPtr& pObject) :
			m_pInterface(NULL)
		{
			QueryVInterface(pObject);
		}
	explicit InterfacePtr(IObject* pObject) :
			m_pInterface(NULL)
		{
			QueryVInterface(pObject);
		}
	virtual ~InterfacePtr()
		{
			Unload();
		}
public:
	inline IF_Type* P()const
		{
			return m_pInterface;
		}
	inline IF_Type* operator->()const
		{
			return m_pInterface;
		}
	inline operator IF_Type*()const
		{
			return m_pInterface;
		}
	inline IF_Type** operator&()
		{
			ASSERT(m_pInterface==NULL);
			return &m_pInterface;
		}
	inline IInterface** Address()
		{
			ASSERT(m_pInterface==NULL);
			return reinterpret_cast<IInterface**>(&m_pInterface);
		}
	vresult QueryVInterface(IObject* pObject)
		{
			IInterface* pIF=NULL;
			if(pObject)
			{
				vresult vr=pObject->QueryVInterface(IID_IF,&pIF);
				if(vr!=vok)
					return vr;
				ASSERT(pIF!=NULL);
				if(pIF==static_cast<IInterface*>(m_pInterface))
				{
					pIF->DecInterfaceRef();
					return vok;
				}
			}
			Unload();
			m_pInterface=static_cast<IF_Type*>(pIF);
			return vok;
		}
	inline vresult QueryVInterface(const ObjectPtr& p)
		{
			return p.QueryVInterface(*this);
		}

	inline const thisClass& operator=(const thisClass& o)
		{
			Load(o.m_pInterface);
			return *this;
		}
	inline const thisClass& operator=(IF_Type* pInterface)
		{
			Load(pInterface);
			return *this;
		}
	inline operator bool()const
		{
			return m_pInterface!=NULL;
		}
	inline bool operator!()const
		{
			return m_pInterface==NULL;
		}
	inline void ReleaseVInterface()
		{
			Unload();
		}
	inline IF_Type* DetachVInterface()
		{
			IF_Type* pIF=m_pInterface;
			m_pInterface=NULL;
			return pIF;
		}
	inline void AttachVInterface(IF_Type* pIF)
		{
			ASSERT(m_pInterface==NULL);
			m_pInterface=pIF;
		}
	inline bool IsNull()const
		{
			return m_pInterface==NULL;
		}
	inline bool IsNotNull()const
		{
			return m_pInterface!=NULL;
		}
private:
	void Unload()
		{
			if(m_pInterface)
			{
				m_pInterface->DecInterfaceRef();
				m_pInterface=NULL;
			}
		}
	void Load(IF_Type*	pIF)
		{
			if(m_pInterface!=pIF)
			{
				Unload();
				m_pInterface=pIF;
				m_pInterface->IncInterfaceRef();
			}
		}
protected:
	IF_Type*	m_pInterface;
	friend class ObjectPtr;
};

class ObjectPtr
{
public:
	explicit ObjectPtr(IObject* pObject=NULL):
			m_pObject(pObject),
			m_bOwn(true)
		{
		}
	ObjectPtr(const ObjectPtr& o):
			m_pObject(o.m_pObject),
			m_bOwn(o.m_bOwn)
		{
			o.m_bOwn=false;
		}
	virtual ~ObjectPtr()
		{
			SetNull();
		}
public:
	const ObjectPtr& operator=(const ObjectPtr& o)
		{
			if(m_pObject==o.m_pObject)
			{
				if(o.m_bOwn)
					m_bOwn=true;
			}
			else
			{
				SetNull();
				m_pObject=o.m_pObject;
				m_bOwn=o.m_bOwn;
			}
			o.m_bOwn=false;
			return *this;
		}
	inline IObject* P()const
		{
			return m_pObject;
		}
	inline IObject** Address()
		{
			ASSERT(m_pObject==NULL);
			return &m_pObject;
		}

	template<class IF_Type>
	inline vresult QueryVInterface(InterfacePtr<IF_Type>& pIF)const
		{
			return pIF.QueryVInterface(m_pObject);
		}
	template<class IF_Type>
	vresult QueryVInterface(IF_Type** ppIF)const
		{
			ASSERT(ppIF && (*ppIF)==NULL);
			IInterface* pIF=NULL;
			if(m_pObject)
			{
				vresult vr=m_pObject->QueryVInterface(IF_Type::IID_IF,&pIF);
				if(vr!=vok)
					return vr;
				ASSERT(pIF);
			}
			*ppIF=static_cast<IF_Type*>(pIF);
			return vok;
		}

	template<class IF_Type>
	bool DoQueryInterfaceInThisClass(viid viid,IF_Type** ppIF)
	{					  
		ASSERT(ppIF && (*ppIF)==NULL);
		IInterface* pIF=NULL;
		if(m_pObject)
		{
			vresult vr=m_pObject->QueryVInterface(viid,&pIF);
			if(vr!=vok)
				return false;			
		}
		ASSERT(pIF!=NULL);
		*ppIF=static_cast<IF_Type*>(pIF);
		return true;
	}

	inline void AttachObject(IObject* pObject,bool bOwn)
		{
			ASSERT(m_pObject==NULL);
			m_pObject=pObject;
			m_bOwn=bOwn;
		}
	inline void AttachObject(IInterface* pIF,bool bOwn)
		{
			ASSERT(m_pObject==NULL);
			ASSERT(pIF);
			pIF->QueryObject(&m_pObject);
			m_bOwn=bOwn;
		}
	inline vresult CreateObject(vclsid clsID)
		{
			SetNull();
			return ::CreateObject(clsID,&m_pObject);
		}
	virtual void SetNull()
		{
			if(m_pObject && m_bOwn)
				m_pObject->ReleaseObject();
			m_pObject=NULL;
		}
	inline bool IsNull()const
		{
			return m_pObject==NULL;
		}
	inline bool IsNotNull()const
		{
			return m_pObject!=NULL;
		}
	inline operator bool()const
		{
			return m_pObject!=NULL;
		}
	IObject* DetachObject()
		{
			IObject* pObj=m_pObject;
			if(pObj)
			{
				m_bOwn=false;
				m_pObject=NULL;
			}
			return pObj;
		}
protected:
	mutable bool m_bOwn;
	IObject* m_pObject;
};

SYS_END()